package rtp;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Panel;
import java.util.Vector;
import javax.media.ControllerErrorEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.Manager;
import javax.media.Player;
import javax.media.RealizeCompleteEvent;
import javax.media.control.BufferControl;
import javax.media.protocol.DataSource;
import javax.media.rtp.Participant;
import javax.media.rtp.RTPControl;
import javax.media.rtp.RTPManager;
import javax.media.rtp.ReceiveStream;
import javax.media.rtp.ReceiveStreamListener;
import javax.media.rtp.SessionListener;
import javax.media.rtp.event.ByeEvent;
import javax.media.rtp.event.NewParticipantEvent;
import javax.media.rtp.event.NewReceiveStreamEvent;
import javax.media.rtp.event.ReceiveStreamEvent;
import javax.media.rtp.event.RemotePayloadChangeEvent;
import javax.media.rtp.event.SessionEvent;
import javax.media.rtp.event.StreamMappedEvent;


import net.jxta.protocol.PipeAdvertisement;

public class RTPJxtaMulticastReceive implements ReceiveStreamListener,
		SessionListener, ControllerListener {
	RTPManager[] mgrs = null;

	Vector<PlayerWindow> playerWindows = null;

	boolean dataReceived = false;

	Object dataSync = new Object();
	private PipeAdvertisement dataPipeAdv;
	private PipeAdvertisement ctrlPipeAdv;

	public RTPJxtaMulticastReceive(PipeAdvertisement dataPipeAdv,
			PipeAdvertisement ctrlPipeAdv) {
		this.dataPipeAdv = dataPipeAdv;
		this.ctrlPipeAdv = ctrlPipeAdv;
	}

	public boolean initialize() {
		try {
			this.playerWindows = new Vector<PlayerWindow>();

			this.mgrs = new RTPManager[1];

			for (int i = 0; i < this.mgrs.length; i++) {
				this.mgrs[i] = RTPManager.newInstance();

				this.mgrs[i].addSessionListener(this);

				this.mgrs[i].addReceiveStreamListener(this);

				this.mgrs[i].initialize(new RTPJxtaMulticastSocketAdapter(
						this.dataPipeAdv, this.ctrlPipeAdv));

				BufferControl bc = (BufferControl) this.mgrs[i]
						.getControl("javax.media.control.BufferControl");
				if (bc == null)
					continue;
				bc.setBufferLength(-2L);
			}

		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("Cannot create the RTP Session: "
					+ e.getMessage());
			return false;
		}

		long then = System.currentTimeMillis();

		long waitingPeriod = 30000L;
		try {
			synchronized (this.dataSync) {
				do {
					if (!this.dataReceived) {
						System.err
								.println("  - Waiting for RTP data to arrive");
					}
					this.dataSync.wait(1000L);
					if (this.dataReceived)
						break;
				} while (System.currentTimeMillis() - then < waitingPeriod);
			}

		} catch (Exception localException1) {
			if (!this.dataReceived) {
				System.err.println("No RTP data was received.");
				close();
				return false;
			}
		}
		return true;
	}

	protected void close() {
		for (int i = 0; i < this.playerWindows.size(); i++) {
			try {
				((PlayerWindow) this.playerWindows.elementAt(i)).close();
			} catch (Exception localException) {
			}
		}
		this.playerWindows.removeAllElements();
		for (int i = 0; i < this.mgrs.length; i++)
			if (this.mgrs[i] != null) {
				this.mgrs[i].removeTargets("Closing session from AVReceive3");
				this.mgrs[i].dispose();
				this.mgrs[i] = null;
			}
	}

	public boolean isDone() {
		return this.playerWindows.isEmpty();
	}

	PlayerWindow find(Player p) {
		for (int i = 0; i < this.playerWindows.size(); i++) {
			PlayerWindow pw = (PlayerWindow) this.playerWindows.elementAt(i);
			if (pw.player == p) {
				return pw;
			}
		}
		return null;
	}

	PlayerWindow find(ReceiveStream strm) {
		for (int i = 0; i < this.playerWindows.size(); i++) {
			PlayerWindow pw = (PlayerWindow) this.playerWindows.elementAt(i);
			if (pw.stream == strm) {
				return pw;
			}
		}
		return null;
	}

	public synchronized void update(SessionEvent evt) {
		if ((evt instanceof NewParticipantEvent)) {
			Participant p = ((NewParticipantEvent) evt).getParticipant();
			System.err.println("  - A new participant had just joined: "
					+ p.getCNAME());
		}
	}

	public synchronized void update(ReceiveStreamEvent evt) {
		RTPManager mgr = (RTPManager) evt.getSource();

		Participant participant = evt.getParticipant();

		ReceiveStream stream = evt.getReceiveStream();

		if ((evt instanceof RemotePayloadChangeEvent)) {
			System.err.println("  - Received an RTP PayloadChangeEvent.");
			System.err.println("Sorry, cannot handle payload change.");
			System.exit(0);
		} else {
			if ((evt instanceof NewReceiveStreamEvent)) {
				try {
					stream = ((NewReceiveStreamEvent) evt).getReceiveStream();

					DataSource ds = stream.getDataSource();
					RTPControl ctl = (RTPControl) ds
							.getControl("javax.media.rtp.RTPControl");
					if (ctl != null)
						System.err.println("  - Recevied new RTP stream: "
								+ ctl.getFormat());
					else {
						System.err.println("  - Recevied new RTP stream");
					}

					if (participant == null)
						System.err
								.println("      The sender of this stream had yet to be identified.");
					else {
						System.err.println("      The stream comes from: "
								+ participant.getCNAME());
					}

					Player p = Manager.createPlayer(ds);
					if (p == null) {
						return;
					}
					p.addControllerListener(this);

					p.realize();

					PlayerWindow pw = new PlayerWindow(p, stream);
					this.playerWindows.addElement(pw);
					synchronized (this.dataSync) {
						this.dataReceived = true;
						this.dataSync.notifyAll();
					}
				} catch (Exception e) {
					System.err.println("NewReceiveStreamEvent exception "
							+ e.getMessage());
					return;
				}
			}
			if ((evt instanceof StreamMappedEvent)) {
				if ((stream != null) && (stream.getDataSource() != null)) {
					DataSource ds = stream.getDataSource();

					RTPControl ctl = (RTPControl) ds
							.getControl("javax.media.rtp.RTPControl");
					System.err
							.println("  - The previously unidentified stream ");
					if (ctl != null) {
						System.err.println("      " + ctl.getFormat());
					}
					System.err
							.println("      had now been identified as sent by: "
									+ participant.getCNAME());
				}
			} else if ((evt instanceof ByeEvent)) {
				System.err.println("  - Got \"bye\" from: "
						+ participant.getCNAME());
				PlayerWindow pw = find(stream);
				if (pw != null) {
					pw.close();
					this.playerWindows.removeElement(pw);
				}
			}
		}
	}

	public synchronized void controllerUpdate(ControllerEvent ce) {
		Player p = (Player) ce.getSourceController();

		if (p == null) {
			return;
		}

		if ((ce instanceof RealizeCompleteEvent)) {
			PlayerWindow pw = find(p);
			if (pw == null) {
				System.err.println("Internal error!");
				System.exit(-1);
			}
			pw.initialize();
			pw.setVisible(true);
			p.start();
		}

		if ((ce instanceof ControllerErrorEvent)) {
			p.removeControllerListener(this);
			PlayerWindow pw = find(p);
			if (pw != null) {
				pw.close();
				this.playerWindows.removeElement(pw);
			}
			System.err.println("AVReceive3 internal error: " + ce);
		}
	}

	class PlayerPanel extends Panel {
		Component vc;
		Component cc;

		PlayerPanel(Player p) {
			setLayout(new BorderLayout());
			if ((this.vc = p.getVisualComponent()) != null) {
				add("Center", this.vc);
			}
			if ((this.cc = p.getControlPanelComponent()) != null) {
				add("South", this.cc);
			}
		}

		public Dimension getPreferredSize() {
			int w = 0;
			int h = 0;
			if (this.vc != null) {
				Dimension size = this.vc.getPreferredSize();
				w = size.width;
				h = size.height;
			}
			if (this.cc != null) {
				Dimension size = this.cc.getPreferredSize();
				if (w == 0) {
					w = size.width;
				}
				h += size.height;
			}
			if (w < 160) {
				w = 160;
			}
			return new Dimension(w, h);
		}
	}

	class PlayerWindow extends Frame {
		Player player;
		ReceiveStream stream;

		PlayerWindow(Player p, ReceiveStream strm) {
			this.player = p;
			this.stream = strm;
		}

		public void initialize() {

			add(new RTPJxtaMulticastReceive.PlayerPanel(this.player));
		}

		public void close() {
			this.player.close();
			setVisible(false);
			dispose();
		}

		public void addNotify() {
			super.addNotify();
			pack();
		}
	}

	class SessionLabel {
		public String addr = null;
		public int port;

		SessionLabel(String session) throws IllegalArgumentException {
			String portStr = null;
			int off;
			if ((session != null) && (session.length() > 0)) {
				while ((session.length() > 1) && (session.charAt(0) == '/')) {
					session = session.substring(1);
				}

				off = session.indexOf('/');
				if (off == -1) {
					if (!session.equals(""))
						this.addr = session;
				} else {
					this.addr = session.substring(0, off);
					session = session.substring(off + 1);

					off = session.indexOf('/');
					if ((off == -1) && (!session.equals(""))) {
						portStr = session;
					}

				}

			}

			System.out.println("addr=" + this.addr);
			if (this.addr == null) {
				throw new IllegalArgumentException();
			}

			if (portStr != null)
				try {
					Integer integer = Integer.valueOf(portStr);
					if (integer == null)
						return;
					this.port = integer.intValue();
				} catch (Throwable t) {
					throw new IllegalArgumentException();
				}
			else
				throw new IllegalArgumentException();
		}
	}
}
